import { Address, toNano, fromNano, beginCell } from '@ton/core';
import { MySwap } from '../wrappers/MySwap';
import { NetworkProvider, compile, } from '@ton/blueprint';
import { MyJettonMaster } from '../wrappers/MyJetton_MyJettonMaster';
import { MyJettonWallet } from '../wrappers/MyJetton_MyJettonWallet';

const other = 'EQCW2BeDLMUViUDHT8L6IHaibAIy95S7QgZg2wZdp7WmJC_Q';

const tspa = "kQCrJ-ArHG3LoX_b9Wz7hLVSNeR0_Cz2hSUwashWDavq-r4P";
const tusdt = "kQCymv2MvB5Bb2Jd45D92HaUfmf06CBm-LJMFU7qjeSjX0V3";

const tspa_owner = "kQBhxrSFHGrvX1g5tubQbuePKvBBMtUMxnUMJRxBKjcTKUFd";
const tusdt_owner = "kQBBO0nvoLTgrW2zWRV_MphUUpcuSlAXpDXxXx0y4NsfB8W7";
const jetton_owner = "0QCjPTREUgXS0n1DxX3-clRKW6NIKp5zF8kxoGkYV2b229sN";

const jetton_to = "0QCSSOrjsJdYJZXPTJbfNQ8wZsoycmWdBagwTzYE3ztmjxQE";

const swap = "EQCla-advP_4UGgjaRypsHXxNphdAtGeddYlxyYiNGvaMV_S";

const master_in = tusdt;
const master_out = tspa;

const jetton_in_expect = "100";
const jetton_out_expect = "100";
// 兑换价格比例,20%,即100个out兑换20个in
// 将精度纳入swap_price考虑, in精度为6, out精度为9, out换in, 需要将 swap_price / 1000
// const swap_price = 20_000 / 1000;
// 将精度差纳入swap_price后, 转换率本身的精度被缩小到2位,为了保证转化率的精度,再次增加3位
// 使用高精度转换率
const swap_price = 83_125_000 / 1000;

export async function run(provider: NetworkProvider) {
    //await deploy(provider);
    //await forwardTransfer(provider);
    //await getData(provider);
    await withdraw(provider);
}

export async function deploy(provider: NetworkProvider) {
    const ownerAddress = Address.parse(jetton_owner);
    const inMasterAddress = Address.parse(master_in);
    const outMasterAddress = Address.parse(master_out);
    const mySwapCode = await compile('MySwap');
    const price = BigInt(swap_price);

    const mySwap = provider.open(await MySwap.fromInit(
        ownerAddress, price,
    ));
    
    const inMaster = provider.open(
        MyJettonMaster.fromAddress(inMasterAddress),
    );
    const outMaster = provider.open(
        MyJettonMaster.fromAddress(outMasterAddress),
    );

    const inWallet = await inMaster.getGetWalletAddress(mySwap.address);
    const outWallet = await outMaster.getGetWalletAddress(mySwap.address);
    
    console.log("in: "+inWallet);
    console.log("out: "+outWallet);
    
    await mySwap.send(
        provider.sender(),
        {
            value: toNano('0.05'),
        },
        {
            $$type: 'Deploy',
            queryId: 0n,
            inWallet: inWallet,
            outWallet: outWallet,
        }
    );

    await provider.waitForDeploy(mySwap.address);
    
    await new Promise((resolve) => setTimeout(resolve, 5000));
}

async function forwardTransfer(provider: NetworkProvider) {
    const senderAddress = provider.sender()?.address;
    if (!senderAddress) {
        throw new Error('Sender address is undefined.');
    }
    const inMasterAddress = Address.parse(master_in);
    const outMasterAddress = Address.parse(master_out);
    const inMaster = provider.open(
        MyJettonMaster.fromAddress(inMasterAddress),
    );
    const outMaster = provider.open(
        MyJettonMaster.fromAddress(outMasterAddress),
    );
    // let in_amount = msg.amount * self.price / 100_000_000; in tact
    // outAmount = inAmount * 100_000_000 / swap_price; in ts
    
    const inAmount = 1_000_000 * 2; // usdt
    const outAmount = inAmount * 100_000_000 / swap_price / 1_000_000_000; // spa 
    console.log("outAmount: "+outAmount);
    
    const master = outMaster;
    const transJettonAmount = toNano(outAmount);
    const forwardTonAmt = toNano(0.05);
    
    let jettonWalletAddress = await master.getGetWalletAddress(senderAddress);
    const srcWallet = provider.open(
        MyJettonWallet.fromAddress(jettonWalletAddress),
    );
    console.log('source address: '+senderAddress);
    
    const forwardTargetAddress = Address.parse(jetton_to);
    const targetAddress = Address.parse(swap);
    const targetJettonWalletAddress = await master.getGetWalletAddress(targetAddress);
    const targetJettonWallet = provider.open(
        MyJettonWallet.fromAddress(targetJettonWalletAddress),
    );
    console.log('target address: '+ targetJettonWalletAddress);
    
    /*
     * 第一次jetton转账之前,无法查询jetton余额.因为jetton wallet未部署.
    const walletDataBefore = await targetJettonWallet.getGetWalletData();
    const balanceBefore = walletDataBefore.balance;
    console.log('Current balance:' + fromNano(balanceBefore));
    */

    const nanoTransfer = transJettonAmount;
    const forwardNano = forwardTonAmt;
    const forwardPayload = beginCell()
        .storeUint(1,1)
        .storeRef(
            beginCell()
            .storeUint(0, 32)
            .endCell()
        ).endCell().beginParse();
    const emptyPayload = beginCell()
        .storeUint(0, 1)
        .endCell().beginParse();
    
    await srcWallet.send(
        provider.sender(),
        {
            value: toNano('0.1'),
            // sendMode: SendMode.PAY_GAS_SEPARATELY, fixed 
        },
        {
            $$type: 'JettonTransfer',
            query_id: 101n,
            destination: targetAddress,
            response_destination: senderAddress,
            amount: nanoTransfer,
            custom_payload: beginCell().endCell(),
            forward_ton_amount: forwardNano,
            forward_payload: forwardPayload,
        }
    );

    console.log(`Transfer ${transJettonAmount} to ${targetAddress} and waiting 22s...`);
    await new Promise((resolve) => setTimeout(resolve, 22000));

    const walletDataAfter = await targetJettonWallet.getGetWalletData();
    const balanceAfter = walletDataAfter.balance;
    console.log('Current balance:' + fromNano(balanceAfter));
}

async function withdraw(provider: NetworkProvider) {
    const senderAddress = provider.sender()?.address;
    if (!senderAddress) {
        throw new Error('Sender address is undefined.');
    }
    const mySwap = provider.open(
        MySwap.fromAddress(Address.parse(swap)),
    );
    
    const inMasterAddress = Address.parse(master_in);
    const outMasterAddress = Address.parse(master_out);
    const inMaster = provider.open(
        MyJettonMaster.fromAddress(inMasterAddress),
    );
    const outMaster = provider.open(
        MyJettonMaster.fromAddress(outMasterAddress),
    );
    const inWalletAddress = await inMaster.getGetWalletAddress(mySwap.address);
    const outWalletAddress = await outMaster.getGetWalletAddress(mySwap.address);
    const inWallet = provider.open(
        MyJettonWallet.fromAddress(inWalletAddress),
    );
    const outWallet = provider.open(
        MyJettonWallet.fromAddress(outWalletAddress),
    );
    const inWalletData = await inWallet.getGetWalletData();
    const inBalance = inWalletData.balance;
    const outWalletData = await outWallet.getGetWalletData();
    const outBalance = outWalletData.balance;
    
    await mySwap.send(
        provider.sender(),
        {
            value: toNano('0.1'),
            // sendMode: SendMode.PAY_GAS_SEPARATELY, fixed 
        },
        {
            $$type: 'Withdraw',
            query_id: 101n,
            inAmount: inBalance,
            outAmount: outBalance,
        }
    );
    
    console.log(`Withdraw all and waiting 20s...`);
    await new Promise((resolve) => setTimeout(resolve, 20000));
}


async function getData(provider: NetworkProvider) {
    const senderAddress = provider.sender()?.address;
    if (!senderAddress) {
        throw new Error('Sender address is undefined.');
    }
    const swapAddress = Address.parse(swap);
    const mySwap = provider.open(
        MySwap.fromAddress(swapAddress),
    );
    
    let price = await mySwap.getPrice();
    let inReverse = await mySwap.getInReverse();
    let outReverse = await mySwap.getOutReverse();
    console.log('price:' + price );
    console.log('inReverse:' + inReverse);
    console.log('outReverse:' + outReverse);
    

    const inMasterAddress = Address.parse(master_in);
    const outMasterAddress = Address.parse(master_out);
    const inMaster = provider.open(
        MyJettonMaster.fromAddress(inMasterAddress),
    );
    const outMaster = provider.open(
        MyJettonMaster.fromAddress(outMasterAddress),
    );
    const inWalletAddress = await inMaster.getGetWalletAddress(mySwap.address);
    const outWalletAddress = await outMaster.getGetWalletAddress(mySwap.address);
    const inWallet = provider.open(
        MyJettonWallet.fromAddress(inWalletAddress),
    );
    const outWallet = provider.open(
        MyJettonWallet.fromAddress(outWalletAddress),
    );
    const inWalletData = await inWallet.getGetWalletData();
    const inBalance = inWalletData.balance;
    const outWalletData = await outWallet.getGetWalletData();
    const outBalance = outWalletData.balance;
    console.log('inBalance:' + fromNano(inBalance));
    console.log('outBalance:' + fromNano(outBalance));
}
